﻿using System;
using System.Collections.Generic;
using System.Text;

namespace NFinal.Sql.Like
{
    public class UTF8Util
    {
        public static int Bytes2CodePoint(byte[] buffer,int index)
        {
            byte byte1 = buffer[index];
            int bytes_count = 0;
            int codePoint = 0;
            if ((byte1 & 0x80) == 0)
            {
                bytes_count = 1;
                codePoint = byte1;
            }
            else if ((byte1 & 0xe0) == 0xc0)
            {
                bytes_count = 2;

            }
            else if ((byte1 & 0xf0) == 0xe0)
            {
                bytes_count = 3;

            }
            else if ((byte1 & 0xf8) == 0xf0)
            {
                bytes_count = 4;
            }
            else if ((byte1 & 0xfc) == 0xf8)
            {
                bytes_count = 5;
            }
            else if ((byte1 & 0xfe) == 0xfc)
            {
                bytes_count = 6;
            }
            else
            {
                return 0;
            }
            index += bytes_count;
            return codePoint;
        }

        public static int CodePoint2Bytes(byte[] buffer, int index)
        {
            byte byte1 = buffer[index];
            int bytes_count = 0;
            int codePoint = 0;
            if ((byte1 & 0x80) == 0)
            {
                bytes_count = 1;
            }
            else if ((byte1 & 0xe0) == 0xc0)
            {
                bytes_count = 2;
            }
            else if ((byte1 & 0xf0) == 0xe0)
            {
                bytes_count = 3;
            }
            else if ((byte1 & 0xf8) == 0xf0)
            {
                bytes_count = 4;
            }
            else
            {
                return 0;
            }
            index += bytes_count;
            return codePoint;
        }

        public static string Encode(string text, int[] keys)
        {
            var chars = text.ToCharArray();
            for (int i = 0; i < chars.Length; i++)
            {
                int code = chars[i];
                // U-00000000 - U-0000007F: 0xxxxxxx 
                if (code < 0x0080)
                {
                    int n = 93;
                    if (code < 32 || code == 127 || code == 37/*%*/ || code == 95/*_*/)
                    {
                        continue;
                    }
                    else
                    {
                        if (code < 37)
                        {
                            code -= 32;
                        }
                        else if (code < 95)
                        {
                            code -= 33;
                        }
                        else if (code < 127)
                        {
                            code -= 34;
                        }
                        for (int k = 0; k < keys[0]; k++)
                        {
                            if (code > keys[k+1])
                            {
                                code= code - keys[k + 1] - 1;
                            }
                            else
                            {
                                code = code + (n - keys[k + 1] - 1);
                            }
                            int point = (n-1) >> 1;
                            if (code > point)
                            {
                                code= ((code - point - 1) << 1) + 1;
                            }
                            else
                            {
                                code = (code << 1);
                            }
                        }
                        if (code < 5)
                        {
                            code += 32;
                        }
                        else if (code < 62)
                        {
                            code += 33;
                        }
                        else if (code < 93)
                        {
                            code += 34;
                        }
                    }
                }
                //U-00000080 - U-000007FF: 110xxxxx 10xxxxxx 
                else if (code < 0x0800)
                {
                    int n = 1920;
                    code -= 0x80;
                    for (int k = 0; k < keys[0]; k++)
                    {
                        if (code > keys[keys[0] + k + 1])
                        {
                            code = code - keys[keys[0] + k + 1] - 1;
                        }
                        else
                        {
                            code = code + (n - keys[keys[0] + k + 1] - 1);
                        }
                        int point = (n - 1) >> 1;
                        if (code > point)
                        {
                            code = ((code - point - 1) << 1) + 1;
                        }
                        else
                        {
                            code = (code << 1);
                        }
                    }
                    code += 0x80;
                }
                //U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx 
                else if (code < 0x10000)
                {
                    int n = 63488;
                    code -= 0x800;
                    for (int k = 0; k < keys[0]; k++)
                    {
                        if (code > keys[(keys[0] << 1) + k + 1])
                        {
                            code = code - keys[(keys[0] << 1) + k + 1] - 1;
                        }
                        else
                        {
                            code = code + (n - keys[(keys[0] << 1) + k + 1] - 1);
                        }
                        int point = (n - 1) >> 1;
                        if (code > point)
                        {
                            code = ((code - point - 1) << 1) + 1;
                        }
                        else
                        {
                            code = (code << 1);
                        }
                    }
                    code += 0x800;
                }
                //U-00010000 - U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 
                else if (code < 0x200000)
                {
                    int n = 2031616;
                    code -= 0x10000;
                    for (int k = 0; k < keys[0]; k++)
                    {
                        if (code > keys[(keys[0] << 1) + k + 2])
                        {
                            code = code - keys[(keys[0] << 1) + k + 2] - 1;
                        }
                        else
                        {
                            code = code + (n - keys[(keys[0] << 1) + k + 2] - 1);
                        }
                        int point = (n - 1) >> 1;
                        if (code > point)
                        {
                            code = ((code - point - 1) << 1) + 1;
                        }
                        else
                        {
                            code = (code << 1);
                        }
                    }
                    code += 0x10000;
                }
                //U-00200000 - U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 
                else if (code < 0x4000000)
                {
                    int n = 65011711;
                    code -= 0x200000;
                    for (int k = 0; k < keys[0]; k++)
                    {
                        if (code > keys[(keys[0] << 2) + k + 1])
                        {
                            code = code - keys[(keys[0] << 1) + k + 1] - 1;
                        }
                        else
                        {
                            code = code + (n - keys[(keys[0] << 1) + k + 1] - 1);
                        }
                        int point = (n - 1) >> 1;
                        if (code > point)
                        {
                            code = ((code - point - 1) << 1) + 1;
                        }
                        else
                        {
                            code = (code << 1);
                        }
                    }
                    code += 0x200000;
                }
                //U-04000000 - U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 
                else
                {
                    int n = 2080374784;
                    code -= 0x4000000;
                    for (int k = 0; k < keys[0]; k++)
                    {
                        if (code > keys[(keys[0] << 2) + k + 2])
                        {
                            code = code - keys[(keys[0] << 2) + k + 2] - 1;
                        }
                        else
                        {
                            code = code + (n - keys[(keys[0] << 2) + k + 2] - 1);
                        }
                        int point = (n - 1) >> 1;
                        if (code > point)
                        {
                            code = ((code - point - 1) << 1) + 1;
                        }
                        else
                        {
                            code = (code << 1);
                        }
                    }
                    code += 0x4000000;
                }
                chars[i] = (char)code;
            }
            return new string(chars);
        }

        public static string Encode(string text,UTF8Key cutCardKey)
        {
            var chars = text.ToCharArray();
            for (int i = 0; i < chars.Length; i++)
            {
                int code = chars[i];
                // U-00000000 - U-0000007F: 0xxxxxxx 
                if (code < 0x0080)
                {
                    int n = 93;
                    if (code < 32 || code == 127 || code == 37/*%*/ || code == 95/*_*/)
                    {
                        continue;
                    }
                    else
                    {
                        if (code < 37)
                        {
                            code -= 32;
                        }
                        else if (code < 95)
                        {
                            code -= 33;
                        }
                        else if (code < 127)
                        {
                            code -= 34;
                        }
                        for (int k = 0; k < cutCardKey.Keys1.Length; k++)
                        {
                            int temp = CutCardUtil.Encode(n, cutCardKey.Keys1[k], code);
                            code = ShuffleCardUtil.Encode(n, temp);
                        }
                        //int v1 = CutCardUtil.Encode(n, 5, code);
                        //code = ShuffleCardUtil.Encode(n, v1);
                        if (code < 5)
                        {
                            code += 32;
                        }
                        else if (code < 62)
                        {
                            code += 33;
                        }
                        else if (code < 93)
                        {
                            code += 34;
                        }
                    }
                }
                //U-00000080 - U-000007FF: 110xxxxx 10xxxxxx 
                else if (code < 0x0800)
                {
                    int n = 1920;
                    code -= 0x80;
                    for (int k = 0; k < cutCardKey.Keys2.Length; k++)
                    {
                        int temp = CutCardUtil.Encode(n, cutCardKey.Keys2[k], code);
                        code = ShuffleCardUtil.Encode(n, temp);
                    }
                    code += 0x80;
                }
                //U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx 
                else if (code < 0x10000)
                {
                    int n = 63488;
                    code -= 0x800;
                    for (int k = 0; k < cutCardKey.Keys3.Length; k++)
                    {
                        int temp = CutCardUtil.Encode(n, cutCardKey.Keys3[k], code);
                        code = ShuffleCardUtil.Encode(n, temp);
                    }
                    code += 0x800;
                }
                //U-00010000 - U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 
                else if (code < 0x200000)
                {
                    int n = 2031616;
                    code -= 0x10000;
                    for (int k = 0; k < cutCardKey.Keys4.Length; k++)
                    {
                        int temp = CutCardUtil.Encode(n, cutCardKey.Keys4[k], code);
                        code = ShuffleCardUtil.Encode(n, temp);
                    }
                    code += 0x10000;
                }
                //U-00200000 - U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 
                else if (code < 0x4000000)
                {
                    int n = 65011711;
                    code -= 0x200000;
                    for (int k = 0; k < cutCardKey.Keys5.Length; k++)
                    {
                        int temp = CutCardUtil.Encode(n, cutCardKey.Keys5[k], code);
                        code = ShuffleCardUtil.Encode(n, temp);
                    }
                    code += 0x200000;
                }
                //U-04000000 - U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 
                else
                {
                    int n = 2080374784;
                    code -= 0x4000000;
                    for (int k = 0; k < cutCardKey.Keys6.Length; k++)
                    {
                        int temp = CutCardUtil.Encode(n, cutCardKey.Keys6[k], code);
                        code = ShuffleCardUtil.Encode(n, temp);
                    }
                    code += 0x4000000;
                }
                chars[i] = (char)code;
            }
            return new string(chars);
        }

        public static string Decode(string text, int[] keys)
        {
            var chars = text.ToCharArray();
            for (int i = 0; i < chars.Length; i++)
            {
                int code = chars[i];
                // U-00000000 - U-0000007F: 0xxxxxxx 
                if (code < 0x0080)
                {
                    int n = 93;
                    if (code < 32 || code == 127 || code == 37/*%*/ || code == 95/*_*/)
                    {
                        continue;
                    }
                    else
                    {
                        //32=>0,37=>5,95=>61
                        if (code < 37)
                        {
                            code -= 32;
                        }
                        else if (code < 95)
                        {
                            code -= 33;
                        }
                        else if (code < 127)
                        {
                            code -= 34;
                        }
                        for (int k = keys[0] - 1; k > -1; k--)
                        {
                            int point = (n - 1) >> 1;
                            if ((code & 1) == 0)
                            {
                                code = code >> 1;
                            }
                            else
                            {
                                code = ((code - 1) >> 1) + point + 1;
                            }
                            if (code < (n - 1 - keys[k+1]))
                            {
                                code = code + keys[k+1] + 1;
                            }
                            else
                            {
                                code = code - (n - keys[k+1] - 1);
                            }
                        }
                        if (code < 5)
                        {
                            code += 32;
                        }
                        else if (code < 62)
                        {
                            code += 33;
                        }
                        else if (code < 93)
                        {
                            code += 34;
                        }
                    }
                }
                //U-00000080 - U-000007FF: 110xxxxx 10xxxxxx 
                else if (code < 0x0800)
                {
                    int n = 1920;
                    code -= 0x80;
                    for (int k = keys[0] - 1; k > -1; k--)
                    {
                        int point = (n - 1) >> 1;
                        if ((code & 1) == 0)
                        {
                            code = code >> 1;
                        }
                        else
                        {
                            code = ((code - 1) >> 1) + point + 1;
                        }
                        if (code < (n - 1 - keys[keys[0]+ k + 1]))
                        {
                            code = code + keys[keys[0] + k + 1] + 1;
                        }
                        else
                        {
                            code = code - (n - keys[keys[0] + k + 1] - 1);
                        }
                    }
                    code += 0x80;
                }
                //U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx 
                else if (code < 0x10000)
                {
                    int n = 63488;
                    code -= 0x800;
                    for (int k = keys[0] - 1; k > -1; k--)
                    {
                        int point = (n - 1) >> 1;
                        if ((code & 1) == 0)
                        {
                            code = code >> 1;
                        }
                        else
                        {
                            code = ((code - 1) >> 1) + point + 1;
                        }
                        if (code < (n - 1 - keys[(keys[0]<<1) + k + 1]))
                        {
                            code = code + keys[(keys[0]<<1) + k + 1] + 1;
                        }
                        else
                        {
                            code = code - (n - keys[(keys[0]<<1) + k + 1] - 1);
                        }
                    }
                    code += 0x800;
                }
                //U-00010000 - U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 
                else if (code < 0x200000)
                {
                    int n = 2031616;
                    code -= 0x10000;
                    for (int k = keys[0] - 1; k > -1; k--)
                    {
                        int point = (n - 1) >> 1;
                        if ((code & 1) == 0)
                        {
                            code = code >> 1;
                        }
                        else
                        {
                            code = ((code - 1) >> 1) + point + 1;
                        }
                        if (code < (n - 1 - keys[(keys[0] << 1) + k + 2]))
                        {
                            code = code + keys[(keys[0] << 1) + k + 2] + 1;
                        }
                        else
                        {
                            code = code - (n - keys[(keys[0] << 1) + k + 2] - 1);
                        }
                    }
                    code += 0x10000;
                }
                //U-0020 0000 - U-03FF FFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 
                else if (code < 0x4000000)
                {
                    int n = 65011712;
                    code -= 0x200000;
                    for (int k = keys[0] - 1; k > -1; k--)
                    {
                        int point = (n - 1) >> 1;
                        if ((code & 1) == 0)
                        {
                            code = code >> 1;
                        }
                        else
                        {
                            code = ((code - 1) >> 1) + point + 1;
                        }
                        if (code < (n - 1 - keys[(keys[0] << 2) + k + 1]))
                        {
                            code = code + keys[(keys[0] << 2) + k + 1] + 1;
                        }
                        else
                        {
                            code = code - (n - keys[(keys[0] << 2) + k + 1] - 1);
                        }
                    }
                    code += 0x200000;
                }
                //U-0400 0000 - U-7FFF FFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 
                else
                {
                    int n = 2080374784;
                    code -= 0x4000000;
                    for (int k = keys[0] - 1; k > -1; k--)
                    {
                        int point = (n - 1) >> 1;
                        if ((code & 1) == 0)
                        {
                            code = code >> 1;
                        }
                        else
                        {
                            code = ((code - 1) >> 1) + point + 1;
                        }
                        if (code < (n - 1 - keys[(keys[0] << 2) + k + 2]))
                        {
                            code = code + keys[(keys[0] << 2) + k + 2] + 1;
                        }
                        else
                        {
                            code = code - (n - keys[(keys[0] << 2) + k + 2] - 1);
                        }
                    }
                    code += 0x4000000;
                }
                chars[i] = (char)code;
            }
            return new string(chars);
        }

        public static string Decode(string text, UTF8Key cutCardKey)
        {
            var chars = text.ToCharArray();
            for (int i = 0; i < chars.Length; i++)
            {
                int code = chars[i];
                // U-00000000 - U-0000007F: 0xxxxxxx 
                if (code < 0x0080)
                {
                    int n = 93;
                    if (code < 32 || code == 127 || code == 37/*%*/ || code == 95/*_*/)
                    {
                        continue;
                    }
                    else
                    {
                        //32=>0,37=>5,95=>61
                        if (code < 37)
                        {
                            code -= 32;
                        }
                        else if (code < 95)
                        {
                            code -= 33;
                        }
                        else if (code < 127)
                        {
                            code -= 34;
                        }
                        for (int k = cutCardKey.Keys1.Length-1; k >-1; k--)
                        {
                            int temp = ShuffleCardUtil.Decode(n, code);
                            code = CutCardUtil.Decode(n, cutCardKey.Keys1[k], temp);
                        }
                        if (code < 5)
                        {
                            code += 32;
                        }
                        else if (code < 62)
                        {
                            code += 33;
                        }
                        else if (code < 93)
                        {
                            code += 34;
                        }
                    }
                }
                //U-00000080 - U-000007FF: 110xxxxx 10xxxxxx 
                else if (code < 0x0800)
                {
                    int n = 1920;
                    code -= 0x80;
                    for (int k = cutCardKey.Keys2.Length - 1; k > -1; k--)
                    {
                        int temp = ShuffleCardUtil.Decode(n, code);
                        code = CutCardUtil.Decode(n, cutCardKey.Keys2[k], temp);
                    }
                    code += 0x80;
                }
                //U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx 
                else if (code < 0x10000)
                {
                    int n = 63488;
                    code -= 0x800;
                    for (int k = cutCardKey.Keys3.Length - 1; k > -1; k--)
                    {
                        int temp = ShuffleCardUtil.Decode(n, code);
                        code = CutCardUtil.Decode(n, cutCardKey.Keys3[k], temp);
                    }
                    code += 0x800;
                }
                //U-00010000 - U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 
                else if (code < 0x200000)
                {
                    int n = 2031616;
                    code -= 0x10000;
                    for (int k = cutCardKey.Keys4.Length - 1; k > -1; k--)
                    {
                        int temp = ShuffleCardUtil.Decode(n, code);
                        code = CutCardUtil.Decode(n, cutCardKey.Keys4[k], temp);
                    }
                    code += 0x10000;
                }
                //U-0020 0000 - U-03FF FFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 
                else if (code < 0x4000000)
                {
                    int n = 65011712;
                    code -= 0x200000;
                    for (int k = cutCardKey.Keys5.Length - 1; k > -1; k--)
                    {
                        int temp = ShuffleCardUtil.Decode(n, code);
                        code = CutCardUtil.Decode(n, cutCardKey.Keys5[k], temp);
                    }
                    code += 0x200000;
                }
                //U-0400 0000 - U-7FFF FFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 
                else
                {
                    int n = 2080374784;
                    code -= 0x4000000;
                    for (int k = cutCardKey.Keys6.Length - 1; k > -1; k--)
                    {
                        int temp = ShuffleCardUtil.Decode(n, code);
                        code = CutCardUtil.Decode(n, cutCardKey.Keys6[k], temp);
                    }
                    code += 0x4000000;
                }
                chars[i] = (char)code;
            }
            return new string(chars);
        }
    }
}
